﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Streambolics.App
{
    public class ModuleManager : PolledComputingModule
    {
        private Dictionary<ComputingModule, ModuleRecoveryHistory> _Histories = new Dictionary<ComputingModule, ModuleRecoveryHistory> ();
        private List<ComputingModule> _AutoStartModules = new List<ComputingModule> ();
        private List<ComputingModule> _AutoStopModules = new List<ComputingModule> ();
        private List<ModuleRecoveryStrategy> _Strategies = new List<ModuleRecoveryStrategy> ();

        /// <summary>
        ///     Add a module that must be kept started.
        /// </summary>
        /// <param name="aModule">
        ///     The module to monitor;
        /// </param>
        /// <remarks><para>
        ///     The module manager will regularly verify that the module is
        ///     started and, if not, will attempt to restart it.
        /// </para><para>
        ///     If the module is failed, it will not be restarted unless the
        ///     failure has been recovered.
        /// </para></remarks>

        public void AddAutoStartModule (ComputingModule aModule)
        {
            _AutoStartModules.Add (aModule);
        }

        public void RemoveAutoStartModule (ComputingModule aModule)
        {
            _AutoStartModules.Remove (aModule);
        }

        public void AddAutoStopModule (ComputingModule aModule)
        {
            _AutoStopModules.Add (aModule);
        }

        public void RemoveAutoStopModule (ComputingModule aModule)
        {
            _AutoStopModules.Remove (aModule);
        }

        protected override void DoStart ()
        {
            DeclareStarted ();
        }

        protected override void DoStop ()
        {
            DeclareStopped ();
        }

        /// <summary>
        ///     Attempt the recovery of a failed module.
        /// </summary>
        /// <param name="aModule">
        ///     The module to recover
        /// </param>

        public void AttemptRecovery (ComputingModule aModule)
        {
            System.Diagnostics.Debug.Assert (aModule.IsFailed, "Attempted the recovery of a module that is not failed");

            ModuleRecoveryHistory history = AccessHistory (aModule);
            foreach (ModuleRecoveryStrategy s in _Strategies)
            {
                if (history.AttemptRecovery (s))
                {
                    // TODO : Call RecoveryAttempted here
                    return;
                }
            }
        }

        private ModuleRecoveryHistory AccessHistory (ComputingModule aModule)
        {
            if (_Histories.ContainsKey (aModule))
            {
                return _Histories[aModule];
            }
            else if (aModule.IsFailed)
            {
                ModuleRecoveryHistory h = new ModuleRecoveryHistory (aModule);
                _Histories.Add (aModule, h);
                return h;
            }
            else
            {
                return null;
            }
        }

        protected override void DoPoll ()
        {
            foreach (ComputingModule m in _AutoStartModules)
            {
                if (m.Status == ComputingModuleStatus.cmsIdle)
                {
                    m.Start ();
                    // TODO : Call ModuleStarted event here
                }
            }

            foreach (ComputingModule m in _AutoStopModules)
            {
                if (m.Status == ComputingModuleStatus.cmsRunning && m.AllChildrenIdle)
                {
                    m.Stop ();
                    // TODO : Call ModuleStopped event
                }
            }

            foreach (ComputingModule m in MasterComputingModule.MasterModule.Children)
            {
                if (m.IsFailed)
                {
                    AttemptRecovery (m);
                }
                else
                {
                    if (_Histories.ContainsKey (m) && _Histories[m].MayForget ())
                    {
                        _Histories.Remove (m);
                        // TODO : Call ModuleRecovered event
                    }
                }
            }
        }
    }
}
